/*
 * Copyright 2022-2029 heshuixing(https://gitee.com/heshuixing)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mercury.ec.module.air.service.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.mercury.ec.framework.air.core.channel.service.AirChannelService;
import com.mercury.ec.framework.air.core.client.AirClient;
import com.mercury.ec.framework.air.core.client.dto.order.cancel.AirOrderCancelReqDTO;
import com.mercury.ec.framework.air.core.client.dto.order.cancel.AirOrderCancelRespDTO;
import com.mercury.ec.framework.air.core.client.dto.order.create.AirOrderCreateReqDTO;
import com.mercury.ec.framework.air.core.client.dto.order.create.AirOrderCreateRespDTO;
import com.mercury.ec.framework.air.core.client.dto.order.issue.AirOrderIssueReqDTO;
import com.mercury.ec.framework.air.core.client.dto.order.issue.AirOrderIssueRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.detr.AirDetrReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.detr.AirDetrRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.flight.AirFlightRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.flight.AirSearchFlightReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.flight.AirSearchFlightRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.pat.AirPatReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.pat.AirPatRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.patex.AirPatexReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.patex.AirPatexRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.price.AirPriceRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.price.AirSearchPriceReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.price.AirSearchPriceRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.raw.AirRawReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.raw.AirRawRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.rt.AirRtReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.rt.AirRtRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.rtkt.AirRtktReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.rtkt.AirRtktRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.tgqrule.AirSearchTgqRuleReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.tgqrule.AirSearchTgqRuleRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.tgqrule.AirTgqRuleRespDTO;
import com.mercury.ec.framework.air.core.client.dto.search.xsfsi.AirXsfsiReqDTO;
import com.mercury.ec.framework.air.core.client.dto.search.xsfsi.AirXsfsiRespDTO;
import com.mercury.ec.framework.air.core.client.impl.sunstn.SunstnAirClient;
import com.mercury.ec.framework.common.enums.CommonStatusEnum;
import com.mercury.ec.framework.common.util.object.BeanUtils;
import com.mercury.ec.module.air.enums.CustomerAgreementTypeEnum;
import com.mercury.ec.module.basic.controller.admin.channel.vo.channel.ChannelRespVO;
import com.mercury.ec.module.basic.controller.admin.channel.vo.extend.ChannelExtendRespVO;
import com.mercury.ec.module.basic.dal.dataobject.airline.AirlineDO;
import com.mercury.ec.module.basic.dal.dataobject.airport.AirportDO;
import com.mercury.ec.module.basic.dal.dataobject.tgqrule.TgqRuleDO;
import com.mercury.ec.module.basic.enums.BasicAirTicketTypeEnum;
import com.mercury.ec.module.basic.enums.BasicBizScopeTypeEnum;
import com.mercury.ec.module.basic.enums.BasicTgqRuleTypeEnum;
import com.mercury.ec.module.basic.service.airline.AirlineService;
import com.mercury.ec.module.basic.service.airport.AirportService;
import com.mercury.ec.module.basic.service.channel.ChannelService;
import com.mercury.ec.module.basic.service.tgqrule.TgqRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.mercury.ec.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.mercury.ec.module.air.enums.ErrorCodeConstants.*;

/**
 * 机票接口服务实现
 *
 * @author hesx
 */
@Slf4j
@Service
public class AirApiServiceImpl implements AirApiService {

    @Resource
    private AirChannelService airChannelService;
    @Resource
    private AirlineService airlineService;
    @Resource
    private ChannelService channelService;
    @Resource
    private AirportService airportService;
    @Resource
    private TgqRuleService tgqRuleService;

    @Override
    public AirSearchFlightRespDTO getFlight(AirSearchFlightReqDTO reqDTO) {
        List<AirlineDO> airlineDOS = airlineService.getAll();
        if (CustomerAgreementTypeEnum.isGp(reqDTO.getCustomerAgreementType())) {
            String airlines = airlineDOS.stream().map(AirlineDO::getNo).collect(Collectors.joining(","));
            reqDTO.setAirlineNo(airlines);
        }
        List<AirClient> airClients = airChannelService.getAirSearchClients();
        AirSearchFlightRespDTO airSearchFlightRespDTO = new AirSearchFlightRespDTO();
        List<AirFlightRespDTO> flights = new ArrayList<>();
        for (AirClient airClient : airClients) {
            try {
                airSearchFlightRespDTO = airClient.getFlight(reqDTO);
                flights.addAll(airSearchFlightRespDTO.getFlights());
            } catch (Exception e) {
                log.error("机票渠道【{}】航班查询异常", airClient.getName(), e);
            }
        }
        // 航班去重
        flights = flights.stream().collect(Collectors.groupingBy(p -> p.getAirlineNo() + p.getFlightNo(), Collectors.minBy(Comparator.comparing(AirFlightRespDTO::getLowestPrice)))).entrySet().stream().map(e -> e.getValue().get()).collect(Collectors.toList());
        // 匹配航司信息
        Map<String, AirlineDO> airlineMap = airlineDOS.stream().collect(Collectors.toMap(AirlineDO::getNo, v -> v, (v1, v2) -> v1));
        List<AirportDO> airportDOS = airportService.getAll();
        Map<String, AirportDO> airportMap = airportDOS.stream().collect(Collectors.toMap(AirportDO::getNo, v -> v, (v1, v2) -> v1));
        flights.forEach(p -> {
            AirlineDO airlineDO = airlineMap.get(p.getAirlineNo());
            if (airlineDO != null) {
                p.setAirlineName(airlineDO.getName());
                p.setAirlineLogo(airlineDO.getLogo());
            }
            if (StrUtil.isNotBlank(p.getCarrierAirlineNo())) {
                airlineDO = airlineMap.get(p.getCarrierAirlineNo());
                if (airlineDO != null) {
                    p.setCarrierAirlineName(airlineDO.getName());
                    p.setCarrierAirlineLogo(airlineDO.getLogo());
                }
            }
            AirportDO departAirportDO = airportMap.get(p.getDepart());
            if (departAirportDO != null) {
                p.setDepartAirportName(departAirportDO.getName());
            }
            AirportDO arriveAirportDO = airportMap.get(p.getArrive());
            if (arriveAirportDO != null) {
                p.setArriveAirportName(arriveAirportDO.getName());
            }
        });
        // 按价格排序
        Collections.sort(flights, Comparator.comparing(AirFlightRespDTO::getLowestPrice));
        airSearchFlightRespDTO.setFlights(flights);
        return airSearchFlightRespDTO;
    }

    @Override
    public AirSearchPriceRespDTO getPrice(AirSearchPriceReqDTO reqDTO) {
        List<AirClient> airClients = airChannelService.getAirSearchClients();
        AirSearchPriceRespDTO airSearchPriceRespDTO = new AirSearchPriceRespDTO();
        List<AirPriceRespDTO> prices = new ArrayList<>();
        for (AirClient airClient : airClients) {
            try {
                airSearchPriceRespDTO = airClient.getPrice(reqDTO);
                prices.addAll(airSearchPriceRespDTO.getPrices());
            } catch (Exception e) {
                log.error("机票渠道【{}】运价查询异常", airClient.getName(), e);
            }
        }
        // 按价格排序
        Collections.sort(prices, Comparator.comparing(AirPriceRespDTO::getPrice));
        // 匹配舱位等级
        List<TgqRuleDO> tgqRuleDOS = tgqRuleService.getCabinTypeList();
        Map<String, TgqRuleDO> tgqRuleMap = tgqRuleDOS.stream().collect(Collectors.toMap(k -> k.getAirlineNo() + k.getCabin(), v -> v, (v1, v2) -> v1));
        prices.forEach(p -> {
            TgqRuleDO tgqRuleDO = tgqRuleMap.get(reqDTO.getAirlineNo() + p.getCabin());
            if (tgqRuleDO != null) {
                p.setCabinTypeName(tgqRuleDO.getCabinTypeName());
            }
        });
        airSearchPriceRespDTO.setPrices(prices);
        return airSearchPriceRespDTO;
    }

    @Override
    public AirSearchTgqRuleRespDTO getTgqRuleList(AirSearchTgqRuleReqDTO reqDTO) {
        AirSearchTgqRuleRespDTO airSearchTgqRuleRespDTO = new AirSearchTgqRuleRespDTO();
        List<TgqRuleDO> tgqRuleDOS = tgqRuleService.getTgqRuleList(reqDTO.getAirlineNo(), reqDTO.getCabin());
        if (CollUtil.isNotEmpty(tgqRuleDOS)) {
            airSearchTgqRuleRespDTO.setCabinType(tgqRuleDOS.get(0).getCabinType());
            airSearchTgqRuleRespDTO.setCabinTypeName(tgqRuleDOS.get(0).getCabinTypeName());
            airSearchTgqRuleRespDTO.setSwitchDescription(tgqRuleDOS.get(0).getSwitchDescription());
            Map<Integer, List<TgqRuleDO>> typeTgqRuleMap = tgqRuleDOS.stream().collect(Collectors.groupingBy(TgqRuleDO::getType));
            if (typeTgqRuleMap.containsKey(BasicTgqRuleTypeEnum.RETURN.getType())) {
                airSearchTgqRuleRespDTO.setReturnRules(BeanUtils.toBean(typeTgqRuleMap.get(BasicTgqRuleTypeEnum.RETURN.getType()), AirTgqRuleRespDTO.class));
            }
            if (typeTgqRuleMap.containsKey(BasicTgqRuleTypeEnum.CHANGE.getType())) {
                airSearchTgqRuleRespDTO.setChangeRules(BeanUtils.toBean(typeTgqRuleMap.get(BasicTgqRuleTypeEnum.CHANGE.getType()), AirTgqRuleRespDTO.class));
            }
        }
        return airSearchTgqRuleRespDTO;
    }

    @Override
    public AirRtRespDTO rt(AirRtReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.rt(reqDTO);
    }

    @Override
    public List<AirPatRespDTO> pat(AirPatReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.pat(reqDTO);
    }

    @Override
    public List<AirPatexRespDTO> patex(AirPatexReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.patex(reqDTO);
    }

    @Override
    public List<AirXsfsiRespDTO> xsfsi(AirXsfsiReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.xsfsi(reqDTO);
    }

    @Override
    public AirDetrRespDTO detr(AirDetrReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.detr(reqDTO);
    }

    @Override
    public AirRawRespDTO raw(AirRawReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.raw(reqDTO);
    }

    @Override
    public AirRtktRespDTO rtkt(AirRtktReqDTO reqDTO) {
        SunstnAirClient sunstnAirClient = new SunstnAirClient(null);
        sunstnAirClient.init();
        return sunstnAirClient.rtkt(reqDTO);
    }

    @Override
    public AirOrderCreateRespDTO createOrder(AirOrderCreateReqDTO reqDTO) {
        ChannelRespVO channel = channelService.getChannel(reqDTO.getChannelId());
        if (channel == null) {
            throw exception(AIR_API_SEARCH_CHANNEL_NOT_EXISTS, reqDTO.getChannelId());
        }
        if (CommonStatusEnum.isDisable(channel.getStatus())) {
            throw exception(AIR_API_SEARCH_CHANNEL_DISABLED, channel.getName());
        }
        if (channel.getBookChannelId() == null) {
            throw exception(AIR_API_SEARCH_CHANNEL_BOOKING_CHANNEL_NOT_EXISTS, channel.getName());
        }
        AirClient airClient = airChannelService.getAirClient(channel.getBookChannelId());
        if (airClient == null) {
            throw exception(AIR_API_BOOKING_CHANNEL_NOT_EXISTS, channel.getBookChannelId());
        }
        reqDTO.setBookOffice(channel.getOffice());
        AirOrderCreateRespDTO airOrderCreateRespDTO = airClient.createOrder(reqDTO);
        airOrderCreateRespDTO.setBookChannelId(airClient.getId());
        return airOrderCreateRespDTO;
    }

    @Override
    public AirOrderCancelRespDTO cancelOrder(AirOrderCancelReqDTO reqDTO) {
        ChannelRespVO channel = channelService.getChannel(reqDTO.getChannelId());
        if (channel == null) {
            throw exception(AIR_API_CANCEL_CHANNEL_NOT_EXISTS, reqDTO.getChannelId());
        }
        if (CommonStatusEnum.isDisable(channel.getStatus())) {
            throw exception(AIR_API_CANCEL_CHANNEL_DISABLED, channel.getName());
        }
        reqDTO.setOfficeNo(channel.getOffice());
        return airChannelService.getAirClient(reqDTO.getChannelId()).cancelOrder(reqDTO);
    }

    @Override
    public AirOrderIssueRespDTO issueOrder(AirOrderIssueReqDTO reqDTO) {
        ChannelRespVO channel = channelService.getChannel(reqDTO.getChannelId());
        if (channel == null) {
            throw exception(AIR_API_ISSUE_CHANNEL_NOT_EXISTS, reqDTO.getChannelId());
        }
        if (CommonStatusEnum.isDisable(channel.getStatus())) {
            throw exception(AIR_API_ISSUE_CHANNEL_DISABLED, channel.getName());
        }
        reqDTO.setOfficeNo(channel.getOffice());
        List<ChannelExtendRespVO> extendRespVOS = channel.getExtensions().stream().filter(p -> p.getScopeType().equals(reqDTO.getScopeType())).collect(Collectors.toList());
        if (CollUtil.isEmpty(extendRespVOS)) {
            throw exception(AIR_API_ISSUE_CHANNEL_SCOPE_TYPE_NOT_EXISTS, channel.getName(), BasicBizScopeTypeEnum.getByType(reqDTO.getScopeType()).getName());
        }
        Optional<ChannelExtendRespVO> ticketVendingMachineNoExtendRespVOOptional = extendRespVOS.stream().filter(p -> p.getDefaultTicketVendingMachineNo()).findFirst();
        if (!ticketVendingMachineNoExtendRespVOOptional.isPresent()) {
            throw exception(AIR_API_ISSUE_CHANNEL_TICKET_VENDING_MACHINE_NO_NOT_EXISTS, channel.getName());
        }
        if (BasicAirTicketTypeEnum.isUatp(channel.getType())) {
            // UATP 需要CVV参数 CCV:信用卡种类 信用卡号/验证金额/有效期/[货币种类]
            // CCV:TP122000200594946/1260.00/0617/CNY
            Optional<ChannelExtendRespVO> cardNoExtendRespVOOptional = extendRespVOS.stream().filter(p -> p.getDefaultCardNo()).findFirst();
            if (!cardNoExtendRespVOOptional.isPresent()) {
                throw exception(AIR_API_ISSUE_CHANNEL_CARD_NO_NOT_EXISTS, channel.getName());
            }
            if (StrUtil.isBlank(cardNoExtendRespVOOptional.get().getCardNo()) || cardNoExtendRespVOOptional.get().getCardNoValidDate() == null) {
                throw exception(AIR_API_ISSUE_CHANNEL_CARD_NO_VALID_DATE_NOT_EXISTS, channel.getName());
            }
            reqDTO.setCardNo(cardNoExtendRespVOOptional.get().getCardNo());
            reqDTO.setCardNoValidDate(cardNoExtendRespVOOptional.get().getCardNoValidDate());
        }
        reqDTO.setTicketVendingMachineNo(ticketVendingMachineNoExtendRespVOOptional.get().getTicketVendingMachineNo());
        return airChannelService.getAirClient(reqDTO.getChannelId()).issueOrder(reqDTO);
    }
}
